"""GitLab credential management."""

import typing

from cki_lib import misc
from cki_lib.gitlab import get_instance
from cki_lib.gitlab import parse_gitlab_url

from . import gitlab_helpers
from . import secrets
from . import token
from . import utils


@token.register_token('gitlab_project_token')
class GitLabProjectToken(token.Token):
    """GitLab project token."""

    clean_active_versions = 2

    def _create_token(self, token_version: str, meta: dict[str, typing.Any]) -> str:
        """Create a token version."""
        return gitlab_helpers.create_personal_token(*parse_gitlab_url(meta['project_url']), meta)

    def _destroy_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Destroy a token version."""
        _, gl_owner = parse_gitlab_url(meta['project_url'])
        gl_owner.access_tokens.delete(meta['token_id'])

    def _rotate_token(
        self,
        old_version: str,
        new_version: str,
        old_meta: dict[str, typing.Any],
        new_meta: dict[str, typing.Any],
    ) -> str:
        """Rotate a token version."""
        _, gl_project = parse_gitlab_url(old_meta['project_url'])
        gl_token = gl_project.access_tokens.get(old_meta['token_id'])
        gl_token.rotate(expires_at=(misc.now_tz_utc() + utils.DEFAULT_INTERVAL).date().isoformat())

        old_meta['revoked'] = True
        new_meta['token_id'] = gl_token.id
        gitlab_helpers.update_project_token(new_meta, gl_token.token)

        return typing.cast(str, gl_token.token)

    def _update_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Update a token version."""
        gitlab_helpers.update_project_token(
            meta, secrets.secret(self.full_token_name(token_version)))

    def _validate_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Validate a token version."""
        token_secret = secrets.secret(self.full_token_name(token_version))
        gl_instance = get_instance(meta['project_url'], token=token_secret)
        gl_project_token = gl_instance.personal_access_tokens.get('self')
        if gl_project_token.revoked or not gl_project_token.active:
            raise ValueError(f'Revoked token {self.token_group}')


@token.register_token('gitlab_group_token')
class GitLabGroupToken(token.Token):
    """GitLab group token."""

    clean_active_versions = 2

    def _create_token(self, token_version: str, meta: dict[str, typing.Any]) -> str:
        """Create a token version."""
        return gitlab_helpers.create_personal_token(*parse_gitlab_url(meta['group_url']), meta)

    def _destroy_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Destroy a token version."""
        _, gl_owner = parse_gitlab_url(meta['group_url'])
        gl_owner.access_tokens.delete(meta['token_id'])

    def _rotate_token(
        self,
        old_version: str,
        new_version: str,
        old_meta: dict[str, typing.Any],
        new_meta: dict[str, typing.Any],
    ) -> str:
        """Rotate a token version."""
        _, gl_group = parse_gitlab_url(old_meta['group_url'])
        gl_token = gl_group.access_tokens.get(old_meta['token_id'])
        gl_token.rotate(expires_at=(misc.now_tz_utc() + utils.DEFAULT_INTERVAL).date().isoformat())

        old_meta['revoked'] = True
        new_meta['token_id'] = gl_token.id
        gitlab_helpers.update_group_token(new_meta, gl_token.token)

        return typing.cast(str, gl_token.token)

    def _update_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Update a token version."""
        gitlab_helpers.update_group_token(
            meta, secrets.secret(self.full_token_name(token_version)))

    def _validate_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Validate a token version."""
        token_secret = secrets.secret(self.full_token_name(token_version))
        gl_instance = get_instance(meta['group_url'], token=token_secret)
        gl_group_token = gl_instance.personal_access_tokens.get('self')
        if gl_group_token.revoked or not gl_group_token.active:
            raise ValueError(f'Revoked token {self.token_group}')


@token.register_token('gitlab_personal_token')
class GitLabPersonalToken(token.Token):
    """GitLab personal token."""

    clean_active_versions = 2

    def _destroy_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Destroy a token version."""
        secret_token = secrets.secret(self.full_token_name(token_version))
        gl_instance = get_instance(meta['instance_url'], token=secret_token)
        gl_instance.personal_access_tokens.delete('self')

    def _rotate_token(
        self,
        old_version: str,
        new_version: str,
        old_meta: dict[str, typing.Any],
        new_meta: dict[str, typing.Any],
    ) -> str:
        """Rotate a token version."""
        api_token = gitlab_helpers.get_api_token_for_pat(old_meta, {'api'})
        gl_instance = get_instance(old_meta['instance_url'], token=secrets.secret(api_token))
        gl_token = gl_instance.personal_access_tokens.get(old_meta['token_id'])
        gl_token.rotate(expires_at=(misc.now_tz_utc() + utils.DEFAULT_INTERVAL).date().isoformat())

        old_meta['revoked'] = True
        new_meta['token_id'] = gl_token.id
        gitlab_helpers.update_personal_token(new_meta, gl_token.token)

        return typing.cast(str, gl_token.token)

    def _update_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Update a token version."""
        gitlab_helpers.update_personal_token(
            meta, secrets.secret(self.full_token_name(token_version)))

    def _validate_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Validate a token version."""
        token_secret = secrets.secret(self.full_token_name(token_version))
        gl_instance = get_instance(meta['instance_url'], token=token_secret)
        gl_personal_token = gl_instance.personal_access_tokens.get('self')
        if gl_personal_token.revoked or not gl_personal_token.active:
            raise ValueError(f'Revoked token {self.token_group}')


@token.register_token('gitlab_project_deploy_token')
class GitLabProjectDeployToken(token.Token):
    """GitLab project deploy token."""

    clean_active_versions = 1

    def _create_token(self, token_version: str, meta: dict[str, typing.Any]) -> str:
        """Create a token version."""
        return gitlab_helpers.create_deploy_token(parse_gitlab_url(meta['project_url'])[1], meta)

    def _destroy_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Destroy a token version."""
        _, gl_owner = parse_gitlab_url(meta['project_url'])
        gl_owner.deploytokens.delete(meta['token_id'])

    def _update_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Update a token version."""
        gitlab_helpers.update_project_deploy_token(meta)

    def _validate_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Validate a token version."""
        _, gl_project = parse_gitlab_url(meta['project_url'])
        gl_deploy_token = gl_project.deploytokens.get(meta['token_id'])
        if gl_deploy_token.revoked or gl_deploy_token.expired:
            raise ValueError(f'Revoked or expired token {self.token_group}')


@token.register_token('gitlab_group_deploy_token')
class GitLabGroupDeployToken(token.Token):
    """GitLab group deploy token."""

    clean_active_versions = 1

    def _create_token(self, token_version: str, meta: dict[str, typing.Any]) -> str:
        """Create a token version."""
        return gitlab_helpers.create_deploy_token(parse_gitlab_url(meta['group_url'])[1], meta)

    def _destroy_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Destroy a token version."""
        _, gl_owner = parse_gitlab_url(meta['group_url'])
        gl_owner.deploytokens.delete(meta['token_id'])

    def _update_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Update a token version."""
        gitlab_helpers.update_group_deploy_token(meta)

    def _validate_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Validate a token version."""
        _, gl_group = parse_gitlab_url(meta['group_url'])
        gl_deploy_token = gl_group.deploytokens.get(meta['token_id'])
        if gl_deploy_token.revoked or gl_deploy_token.expired:
            raise ValueError(f'Revoked or expired token {self.token_group}')


@token.register_token('gitlab_runner_authentication_token')
class GitLabRunnerAuthenticationToken(token.Token):
    """GitLab runner authentication token."""

    clean_active_versions = 1

    def _update_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Update a token version."""
        gitlab_helpers.update_runner_token(
            meta, secrets.secret(self.full_token_name(token_version)))

    def _validate_token(self, token_version: str, meta: dict[str, typing.Any]) -> None:
        """Validate a token version."""
        token_secret = secrets.secret(self.full_token_name(token_version))
        gl_instance = get_instance(meta['instance_url'])
        gl_instance.http_post('/runners/verify', post_data={"token": token_secret})
